﻿using GameSave.Common.Entities;
using GameSave.Common.Misc;
using GameSave.Infrastructure.Entities;
using GameSave.Infrastructure.Interfaces;
using GameSave.Infrastructure.Message;
using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace GameSave.Impl.JobExecutor
{
    public class FileChangedDependJobExecutor : IJobExecutor
    {
        private Job _currentJob;
        private SysConfig _sysContext;
        private ILog logger = LogManager.GetLogger(nameof(TimerDependJobExecutor));
        private FileSystemWatcherEx _fileSysWatchEx;
        CancellationTokenSource beforeCancelTokenSource;
        CancellationTokenSource afterCancelTokenSource;
        Task BeforeTask = null;
        Task AfterTask = null;
        private DateTime lastFileChangeDateTime;
        private DateTime lastExecuteBackupDateTime;
        private int defaultBackupColdDown = 30;
        private int defaultFileModifyIgnoreTime = 5;

        private string watchDirectory = "";
        private bool watchSystemHasStop = false;
        private Quick.IMessenger messenger;


        public void InitExecutor(JobContext context, SysConfig sysContext, Quick.IMessenger msger)
        {
            _currentJob = context.CurrentJob;
            _sysContext = sysContext;
            messenger = msger;

            if (_sysContext.BackupTriggerColdDown > 0)
            {
                defaultBackupColdDown = _sysContext.BackupTriggerColdDown;
            }
            watchDirectory = Path.GetDirectoryName(_currentJob.TargetBackupFile);
            BuildFileWatcher(watchDirectory);
        }

        private void OnFileCreated(object sender, System.IO.FileSystemEventArgs e)
        {

        }

        private void OnFileChanged(object sender, System.IO.FileSystemEventArgs e)
        {
            string changedFileName = e.FullPath;
            if(_currentJob.TargetBackupFile.Equals(changedFileName))
            {
                //保底CD: 1倍于备份冷却延迟时间，即因冷却间隔或内置CD而不断被丢弃存档变更
                //约定在一段间隔后,强制的执行一次备份存档动作，防止从头到尾备份动作被不停取消，而没有产生任何存档
                int skipIntervalTime = (int)(DateTime.Now - lastExecuteBackupDateTime).TotalSeconds;
                if (skipIntervalTime > defaultBackupColdDown)
                {
                    ResolveChange(e);
                    lastExecuteBackupDateTime = DateTime.Now;
                    Debug.WriteLine("保底备份已执行.");
                    return;
                }

                //内置CD：5秒，即5秒内产生第二次文件变更，无条件的不作处理
                int fileChangeIntervalTime = (int)(DateTime.Now - lastFileChangeDateTime).TotalSeconds;
                lastFileChangeDateTime = DateTime.Now;
                if (fileChangeIntervalTime <= defaultFileModifyIgnoreTime)
                {
                    Debug.WriteLine($"内置CD期间({defaultFileModifyIgnoreTime}秒)发生的FileChanged事件，已忽略.");
                    return;
                }


                //以下的代码可能不会被执行？
                if (BeforeTask == null || BeforeTask.IsCanceled == true || BeforeTask.IsFaulted == true || BeforeTask.IsCompleted == true)
                {
                    beforeCancelTokenSource = new CancellationTokenSource();
                    if (AfterTask != null && !AfterTask.IsCompleted && AfterTask.IsCanceled != false)
                    {
                        afterCancelTokenSource.Cancel();
                        Debug.WriteLine("Cancel After Task;");

                    }
                    BeforeTask = Task.Run(() =>
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(defaultBackupColdDown));
                        if (beforeCancelTokenSource.IsCancellationRequested)
                        {
                            return;
                        }
                        Debug.WriteLine("Execute Before Task;");
                        ResolveChange(e);
                    }, beforeCancelTokenSource.Token);
                }

                else if (AfterTask == null || AfterTask.IsCanceled == true || AfterTask.IsFaulted == true || BeforeTask.IsCompleted == true)
                {
                    //取消先前任务
                    beforeCancelTokenSource.Cancel();
                    Debug.WriteLine("Cancel Before Task;");

                    afterCancelTokenSource = new CancellationTokenSource();
                    AfterTask = Task.Run(() =>
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(defaultBackupColdDown));
                        if (afterCancelTokenSource.IsCancellationRequested)
                        {
                            return;
                        }
                        Debug.WriteLine("Execute After Task;");
                        ResolveChange(e);
                    }, afterCancelTokenSource.Token);
                };

            }
        }

        private void ResolveChange(System.IO.FileSystemEventArgs e)
        {
            if (e.ChangeType == System.IO.WatcherChangeTypes.Created
                || e.ChangeType == System.IO.WatcherChangeTypes.Changed)
            {
                //检测到文件变动，备份当前变动的文件
                string backupBaseDir = _sysContext.SaveDirectory;
                string jobDir = Path.Combine(backupBaseDir, _currentJob.JobId);
                if (!Directory.Exists(jobDir))
                {
                    Directory.CreateDirectory(jobDir);
                }

                DateTime now = DateTime.Now;
                string backUpdirName = $"{now.ToString("yyyyMMdd")}_{now.ToString("HHmmss")}";
                string backUpDir = Path.Combine(jobDir, backUpdirName);
                string backupRelativePath = $"{_currentJob.JobId}\\{backUpdirName}";
                if (!Directory.Exists(backUpDir))
                {
                    Directory.CreateDirectory(backUpDir);
                }
                string backupFileName = Path.GetFileName(e.FullPath);
                string destFileFullPath = Path.Combine(backUpDir, backupFileName);

                File.Copy(e.FullPath, destFileFullPath, true);
                _currentJob.LastExecuteDate = now;
                _currentJob.SaveOperationCount += 1;
                string serialNumText = string.Format("{0:D3}", _currentJob.SaveOperationCount);
                string desc = $"存档{serialNumText}";

                messenger.Send<NewBackupCreateMessage>(new NewBackupCreateMessage
                {
                    NewBackup = new GameSaveModel
                    {
                        CreateDate = now,
                        GameSaveSerialNum = _currentJob.SaveOperationCount,
                        JobID = _currentJob.JobId,
                        Desc = desc,
                        BackupRelativePath = backupRelativePath,
                        IsAutoSave = true
                    }

                });
            }
        }

        public void Start()
        {
            if (_fileSysWatchEx != null && !watchSystemHasStop)
            {
                _fileSysWatchEx.Start();
                logger.Info($"Job已启动(Name='{_currentJob.JobName}',Id='{_currentJob.JobId}')");
            }
            else
            {
                BuildFileWatcher(watchDirectory);
                _fileSysWatchEx.Start();
            }
        }

        private void BuildFileWatcher(string watchDir)
        {
            _fileSysWatchEx = new FileSystemWatcherEx(watchDir,
                "*.*",
                true,
                "",
                OnFileCreated,
                null,
                OnFileChanged);
        }

        public void Stop()
        {
            if (_fileSysWatchEx != null)
            {
                _fileSysWatchEx.Stop();
                watchSystemHasStop = true;
                logger.Info($"Job已停止(Name='{_currentJob.JobName}',Id='{_currentJob.JobId}')");
            }
        }
    }
}
